package com.vision.core.cl;

import com.google.gwt.activity.shared.ActivityManager;
import com.google.gwt.activity.shared.ActivityMapper;
import com.google.gwt.core.client.EntryPoint;
import com.google.gwt.core.client.GWT;
import com.google.gwt.core.client.GWT.UncaughtExceptionHandler;
import com.google.gwt.event.shared.GwtEvent.Type;
import com.google.gwt.place.shared.Place;
import com.google.gwt.place.shared.PlaceChangeEvent;
import com.google.gwt.place.shared.PlaceController;
import com.google.gwt.place.shared.PlaceHistoryHandler;
import com.google.gwt.place.shared.PlaceHistoryHandler.DefaultHistorian;
import com.google.gwt.place.shared.PlaceHistoryHandler.Historian;
import com.google.gwt.user.client.History;
import com.google.web.bindery.event.shared.Event;
import com.google.web.bindery.event.shared.EventBus;
import com.google.web.bindery.event.shared.HandlerRegistration;
import com.sencha.gxt.widget.core.client.container.SimpleContainer;
import com.vision.core.cl.mvp.AppActivityMapper;
import com.vision.core.cl.mvp.AppPlaceHistoryMapper;
import com.vision.core.cl.ui.ApplicationShell;
import com.vision.core.cl.ui.MainView;
import com.vision.core.cl.ui.MenuView;
import com.vision.core.cl.ui.Notifications;

/**
 * Base entry point for applications providing default implementations to start
 * a module.
 * 
 * @author Mark
 *
 */
public class BaseModule implements EntryPoint, MenuView.Presenter, MainView.Presenter {

	protected static ClientFactory factory = GWT.create(ClientFactory.class);

	/**
	 * Sets up the necessary structure for the module to start.
	 */
	@Override
	public void onModuleLoad() {
		// Set the uncaught exception handler
		GWT.setUncaughtExceptionHandler(new UncaughtExceptionHandler() {
			@Override
			public void onUncaughtException(Throwable t) {
				Notifications.unexpectedError(t);
			}
		});
		
		EventBus eventBus = factory.getEventBus();
		PlaceController placeController = factory.getPlaceController();

		// Create the application's widget
		SimpleContainer widget = createAppWidget(factory);

		// Initialize activity mapper and manager
		ActivityMapper activityMapper = new AppActivityMapper(factory);
		ActivityManager activityManager = new ActivityManager(activityMapper, eventBus);
		activityManager.setDisplay(widget);
		
		// Initialize history mapper and place navigation handler
		AppPlaceHistoryMapper historyMapper = GWT.create(AppPlaceHistoryMapper.class);
		Historian historian = (Historian) GWT.create(DefaultHistorian.class);
		PlaceHistoryHandler historyHandler = new PlaceHistoryHandler(historyMapper, historian);
		historyHandler.register(placeController, eventBus, new AppPlace(getDefaultPlaceId()));
		
		// Add the menu and application view to the event bus
		MenuView menuView = factory.getMenuView();
		menuView.setPresenter(this);
		eventBus.addHandler(PlaceChangeEvent.TYPE, menuView);

		MainView mainView = factory.getMainView();
		mainView.setPresenter(this);
		
		String token = History.getToken();
		if (token == null || token.isEmpty()) {
			visit(getDefaultPlaceId());
		} else {
			// Goes to place represented on URL or default place
			historyHandler.handleCurrentHistory();
		}
	}
	
	protected SimpleContainer createAppWidget(ClientFactory factory) {
		ApplicationShell panel = new ApplicationShell(factory.getMenuView(), factory.getMainView());
		return panel;
	}

	@Override
	public void goTo(Place place) {
		factory.getPlaceController().goTo(place);
	}
	
	public String getDefaultPlaceId() {
		return "dashboard";
	}
	
	// Convenience Methods %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
	
	/**
	 * Fires <tt>event</tt> through the module's global {@link EventBus}. 
	 * 
	 * @param event
	 */
	public static void fireEvent(Event<?> event) {
		factory.getEventBus().fireEvent(event);
	}
	
	/**
	 * Adds the <tt>handler</tt> to the module's global {@link EventBus}.
	 * 
	 * @param type
	 * @param handler
	 * @return
	 */
	public static <H> HandlerRegistration addHandler(Type<H> type, H handler) {
		return factory.getEventBus().addHandler(type, handler);
	}
	
	/**
	 * Calls {@link #visit(Place)}, passing an {@link AppPlace} instance constructed
	 * from <tt>place</tt>. 
	 * 
	 * @param place
	 */
	public static void visit(String place) {
		visit(new AppPlace(place));
	}
	
	/**
	 * Goes to the specified <tt>place<tt> using the {@link ClientFactory}'s {@link PlaceController}.
	 * @param place
	 */
	public static void visit(Place place) {
		factory.getPlaceController().goTo(place);
	}
	
}
